Skip to content

fix(raven-design): deck lane on the engine's modules, defaults first, one .pptx delivered - #451

Merged
0xKT merged 20 commits into
mainfrom
fix/ppt_guidance_and_receipt_noise
Sep 16, 2026
Merged

0xKT merged 20 commits into
mainfrom
fix/ppt_guidance_and_receipt_noise

Conversation

@Tchen-data

@Tchen-data Tchen-data commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The hand-built deck lane (Raven-Design at medium and high) and the deck engine lane (Raven-PPT at max) as one product, measured on live runs.

  • image_search for the deck lanes sits behind tools.web.search.images, off by default and switched on by the design launcher; the readiness probe for the deck route reads the target lane's own picture vendor; the build-interpreter shim has a .cmd twin for native Windows.
  • Deck route on Raven-Design: page furniture comes from the engine's helper modules (ppt_layout, ppt_theme, ppt_icons, ppt_shapes, ppt_charts) written beside the build script; diagrams, picture treatment and cover/section compositions are drawn by hand. Five recorded runs had each rewritten the same 48 helpers.
  • Deck route keeps no anti-pattern ledger, contract or task state: nine audited runs wrote the ledger 18 times and read it back never, and every real fix came from reading a render. The editorial skill is read alongside the deck skill; every page is read back from a render at 90 dpi or more before delivery.
  • Defaults before questions: language of the request, general audience, about 20 pages, light ground, memory first; ask at most once. Two of three ask_user calls in the audit timed out unanswered.
  • Formulas as pictures (matplotlib mathtext, dfrac promotion, serif scale) and inline sub/superscript runs, shared by both lanes; image_search for both lanes keyed by the selected search vendor; the raven-python interpreter shim (sh wrapper, not a symlink); all three tiers at 400 iterations.
  • layouts.md gains nine compositions measured off the packaged templates' reference pages; three rules ask for full components, a hierarchy among them, and the whole page used.
  • The max lane delivers the .pptx alone (D64); the PDF render stays the engine's own preview.
  • ppt-engine: template gates silenced on template pages, borrowed template files kept apart from borrowed page numbers, the icon pen follows the icon's side.

Type

  • Fix
  • Feature
  • Docs
  • CI / tooling
  • Refactor
  • Other

Verification

uv run --frozen pytest -q -x tests/test_agents_design_launcher.py tests/test_subagent_routing_backend.py tests/test_ppt_engine_build_tool.py tests/test_ppt_engine_build_stage.py tests/test_ppt_engine_assets_formulas.py tests/test_ppt_engine_gates_registry.py
# 299 passed

Live runs on a test stack (medium tier, glm-5.3-flash, the eight-character request "make a deck introducing computer vision"): with the editorial skill read, 20 pages, 18 of them carrying a searched picture or a paper figure, delivered in 26 minutes with no question asked, no task-state call, no ledger, no hand-written helper, renders at 90 dpi.

  • Relevant tests pass locally
  • Relevant lint / type checks pass locally
  • User-facing docs or screenshots are updated when needed

Risk

User-visible: the max lane no longer delivers a PDF beside the deck; the design lane asks fewer questions; the host loop offers image_search only where tools.web.search.images is on. Rollback: revert the commits touching agents/raven-design, raven/agent and plugins-dist/ppt-engine/raven_ppt/stages/build.py; no data migration is involved.

  • Security impact considered
  • Backward compatibility considered
  • Rollback path is clear for risky changes

Related Issues

N/A

@Tchen-data
Tchen-data requested a review from 0xKT September 16, 2026 14:19

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: two portability and routed-readiness defects must be fixed before merge.

I reviewed the full diff and relevant surrounding code, callers, route architecture, and commit history. I also checked the repository rules and domain terminology, backward compatibility, and whether tests were weakened; I found no weakened tests or separate rule violations worth raising. The two concrete failures are marked inline.

Verification: the directly affected Python suite passed (958 passed, 1 skipped); the broader provider, capability, plugin, publish, and materials suite passed (233 passed); the workspace UI test passed (31 passed) and TypeScript checking passed. Ruff, diff whitespace, source-language, and large-file checks also passed. The skipped Python test requires LibreOffice.

Comment thread raven/agent/loop/wiring.py Resolved
Comment thread agents/raven-design/run.py Resolved

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: the two existing review findings remain unaddressed.

I reviewed the new commit and rechecked the full diff, relevant callers, history, route architecture, repository rules and domain terms, backward compatibility, and test changes. The new tool-filtering entries and regenerated TUI catalogue are consistent and add no new finding, but neither existing blocked code path changed, so both open threads still apply to this revision. No tests were weakened.

Verification: the affected launcher, routing, and web-tool regression set passed (314 passed); TUI i18n tests passed (8 passed), the generated-catalogue check passed, and TUI TypeScript checking passed. Ruff, diff whitespace, source-language, and large-file checks also passed. An initial direct Vitest invocation collected no tests because it bypassed the package's required pretest build; rerunning through the declared npm test script built the dependency and passed.

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; this can merge as far as I am concerned.

Both prior findings are fixed in debc00c, I accepted the author's answers, and I resolved both threads. For this revision I reviewed the five-file fix delta and its callers, relevant history, repository rules and domain terms, backward compatibility on POSIX and Windows, route architecture, and test changes; I found no new issue and no weakened test.

Verification on the exact PR head: 184 affected launcher, routing, and web-tool tests passed. Ruff, diff whitespace, source-language, and large-file checks also passed.

… third of a clone

The whitespace reading groups words into a line by vertical overlap, not by a
4pt top, so a unit beside a figure is no longer an orphan. A full-width panel
that runs off the bottom edge is the page's ground and not body reaching the
boundary, so the trailing field of a cloned page is read again; a centred body
is exempt from it. Cloned pages skip only the header reading, borrowed pages
only the group gaps, and pages built on the template's own cover, index,
divider or closing skip the gate.

Data-source lines count as captions for the type floor. layout_picture is said
once per run. The review ledger refuses an ownership-only reason on a page the
template does not own, counts a reason that made room as a fix, and names the
underfilled reason it refuses.

tidy returns a line whose solid fill names no colour to no line at all, which
is what reading shape.line.color through python-pptx leaves behind. The build
reply folds Python warnings into warned.json, keeps the type_floor and
title_row detail internal, and exempts drafts from the evidence finding. The
script runner notes a SLIDE banner that heads two blocks.

Template compose cuts a cover-fit picture in pixels before filling a shape
with it, since LibreOffice ignores srcRect on shape fills, drops the scale a
template baked into normAutofit, and lets the template tool read borrowed
pages. The outline refuses one repeated page but leaves a run of three alike
pages alone as a series. Prepare, materials and state guidance say where the
figures of a cited paper are and which file a figure id names.
…earch vendor

A picture search tool beside web_search: one call takes every picture the
deck needs as queries, and each hit carries the direct image URL, its pixel
size, the domain and the page it came from; anything under 640px wide is
dropped. The tool is offered only when the vendor with an image surface has a
key, following the host's selected search vendor where that vendor has one and
Serper otherwise, and the capability table swaps in that vendor's config path.

The routed design lane registers it under the same gate, so a host on a
vendor that searches pages only does not open a lane that needs pictures. The
ppt lane keeps its own ppt_image_search and holds the host tool out. The
oversized-picture refusal in the loop says only that the pictures came out.
…ration tiers

The launcher writes <state root>/bin/raven-python, a shell wrapper on its own
interpreter, and appends that directory to tools.exec.pathAppend, so the deck
skill can name one interpreter that has python-pptx and raven_ppt on a uv tool
install, an editable install and a wheel alike. A wrapper and not a symlink:
CPython resolves a symlinked venv python back to the base interpreter. Two
runs spent five and seven exec calls hunting for the interpreter and one
built a venv of its own.

The deck skill reads deck-to-pptx before the first page, searches pictures
through image_search where the deployment has it, crops a paper's figures out
of the PDF with PyMuPDF and renders formulas with mathtext rather than typing
them, then renders the page and looks. The route note says the same.

All three tiers run 400 tool iterations; medium keeps low reasoning effort.
The baseline cap leaves the fork's 150, which the launcher test records.
Tabler draws every icon with a 2-unit stroke on a 24-unit grid, so the weight
is a twelfth of whatever side the icon is drawn at. add_icon's fixed 1.75pt pen
kept that proportion only at 0.29in; at the 0.7in a card carries it was a
third of the designed weight, and the delivered decks' icons read as hairlines
beside the type. The default is now side / 12 in the engine helper, the route
note and the deck skill's reference alike, and a caller who wants a fixed pen
still passes width_pt. The dot a round cap leaves is sized to the same pen.
With no PDF in hand the skill first has the deck find and download one, from
the publisher or arXiv, and crop from that; image_search for the published
figure is the last resort, when no PDF can be had.
…owed page numbers

The whitespace gate's list of borrowed pages was added under the name the
template gates already used for the list of borrowed template files, and the
later definition won: placeholder_copy, placeholder_marks, template_picture
and same_mark received page numbers, raised on Path(4), and check_deck
swallowed the error, so on a deck with a borrowed page none of the four
reported anything. The page list is _borrowed_pages now, and the registry
test asserts that _borrowed still answers with files.

The review tool's house-page reading catches an unreadable template the way
the gate registry does, the made-room refusal branch is covered by a reason
that names ownership and the room made, and a test docstring stops quoting
the icon pen's old default.
…d-built deck lane

raven_ppt.services.assets.formulas gives the deck lane below the top tier
what ppt_layout.formula gives the engine's own scripts: add_formula renders
TeX with mathtext to a transparent PNG in the deck's ink and places it at
true size, with \frac promoted to display style and STIX scaled to stand
level with body copy; math_runs sets a sentence that carries symbols as text
runs with real sub- and superscripts, italic variables and upright words.
CJK inside an expression and constructs mathtext does not know are refused
with the way round. The ppt_layout template promotes \frac the same way.
…s rules only

The raven-python shim is written with the interpreter path shell-quoted and
replaced atomically. The deck skill, its assets reference and the route note
lose every sentence of rationale and keep the rules: light ground by default,
recommended when asking; a paper's figures come from its PDF, a downloaded
one before an image search; add_formula for anything that stacks, math_runs
for symbols in a sentence, no _A or ^2 left as characters; the pen follows
the icon's side.
The lane asked the user for language, audience, length and ground on
most decks. The skill, the route note and the product description now
take each of those from the request, then from what memory recalled
about the user, then from fixed defaults (the request's language, a
general audience, about 20 pages, a light ground), and ask at most once,
only for what none of those settles.

The lane's memory slice hardcoded the stock everos address, so a host
serving everos elsewhere gave the lane an empty recall every time. The
launcher now copies the host's everos base_url into the slice.

The interpreter shim is written through a staged file and os.replace.
The engine lane delivered the PDF render beside the deck and named it
in the reply, so a max deck arrived as two files where a medium or high
deck arrived as one. The render under out/ stays the engine's own
preview and is no longer delivered or handed over; the build tool's
reply, its description and the authoring skill say one file lands.
Recorded as D64 in the design ledger.
…d keeps no ledger

Five recorded deck runs each wrote their own python-pptx kit -- text box with
CJK font, rectangle, rule, connector, icon, header, footer, source note -- 48
helpers in all, with the previous deck's palette baked in. The engine already
writes the same kit beside a build script (ppt_layout, ppt_theme, ppt_icons,
ppt_shapes, ppt_charts), measured and themed; the route note and the deck skill
now say to write those modules once and import from them, and the icon-drawing
snippet they used to copy is gone.

Nine audited runs wrote the anti-pattern ledger 18 times and read it back never,
wrote no contract, and used the task state as progress ticks that leaked across
sessions; every real fix came from reading a render. On this route the note and
the skill now say: no ledger, no contract, no task state, and every page read
back from a render at 90 dpi or more before the file is handed over.

Verified on a live medium run (21 pages, 21 minutes, no question asked): no
task-state call, no ledger, no hand-written helper, renders at 90 dpi, 32 page
reads.
… every page's picture

The first live run under the no-ledger rule read only deck-to-pptx: the model took
"no ledger, no contract, no task state on this route" as leave to skip the engine
skills, and design-editorial-and-presentations is where the content order and the
editorial judgement come from. The deck came out at 8 picture pages of 21 with one
search for application photos. The route note and the skill now name both skills
to read, say the ledger rule excuses nothing else, and add the picture rules: every
page is named with the picture it carries before the build, type-and-card pages are
at most a third of the deck, a model, method, dataset or milestone page carries its
own figure whatever the audience, the first search covers every page and a page
left without one gets a second, count 8 per query.

Second live run, same eight-character request: both skills read, 18 picture pages
of 20, paper figures for AlexNet, CNN, CLIP, YOLO, segmentation, Imagen and the
adversarial panda, 26 minutes including one upstream timeout the host resumed.
…the templates' compositions

Under "helpers are packaged; do not write your own" two live runs drew nothing by
hand: 305 lines of build script, zero shapes, pictures placed bare, ten of twenty
pages the same two-column card layout. The rule now splits the page: furniture
(text, points, cards, rules, heading, footer, table, icon, chart, formula, the
grid) comes from the engine's modules and is not rewritten; what makes a page its
own -- a diagram, a polygon, a map, a custom arrow, a gradient, a hero number, a
cover or section composition, and the treatment of every placed picture (frame,
shadow, scrim, crop, full-bleed ground) -- is drawn by hand with python-pptx.

The picture rule added in the previous commit is withdrawn: its numbers were not
measured. The editorial skill stays required.

layouts.md gains twenty compositions read off the 54 template pages the deck
engine keeps as reference layouts, each with its shares of the canvas and the
template pages it comes from, so a hand-built deck has the same menu of shapes
a template-bound one does.
…rks are searched

The engine table draws the ordinary grid; merged cells, spanning headers, icons or colour inside a cell and over-wide tables are drawn by hand. Generic marks come from the packaged icon set; a company's logo, a product's icon or a framework's badge is searched, downloaded and placed as a picture.
…inside its box

Two rules with the engine gates' own numbers: content runs to within 0.5in of the footer, no gap over 0.75in between groups, no card under 35% full, a short run centred with holding() or given its picture; copy measured before drawing and never shrunk to fit. The engine's whitespace gate read 15 gaps and 3 sparse panels on one delivered medium deck and 14 overflowing boxes on the next; the hand-built lane never runs that gate.
…the page's edges

The previous commit stated the gates' inch thresholds; the rules now say what a page is made of: every component full, components in a hierarchy of size, weight and tint with groups told apart by the space between them, and the page composed to its edges.
…ld shim a .cmd twin

The deck route's readiness probe took the host's selected search vendor as the
one the target lane would search pictures with. Raven-PPT holds the host
image_search out and searches through its own plugin, which speaks Serper, so a
Tavily host on Tavily's key alone opened a lane that could not search. The
probe now asks the target first: a product whose config holds image_search out
is read on its own tools.web.search.provider, Serper when it names none.

The design lane's raven-python shim was a POSIX shell script only; cmd.exe
cannot run one, so a medium or high deck on native Windows failed at its first
interpreter call. A raven-python.cmd twin is written beside it and resolves
through PATHEXT.
The picture search registered in every loop that held a search key, so the
research, code and oncall lanes grew a tool their faces never listed. The tool
now sits behind tools.web.search.images, off by default; the design launcher
switches it on in the config it renders, on top of the host's web slot it
inherits, and the ppt lane keeps holding the host tool out in favour of its
own. A loop that did not ask has no image_search at all, keyed or not.
@Tchen-data
Tchen-data force-pushed the fix/ppt_guidance_and_receipt_noise branch from debc00c to 6995b78 Compare September 16, 2026 14:59
@0xKT
0xKT merged commit 5a29ea2 into main Sep 16, 2026
13 of 19 checks passed
@0xKT
0xKT deleted the fix/ppt_guidance_and_receipt_noise branch September 16, 2026 15:02

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: make the new image-search opt-in agree with capability reporting and its invariant tests.

I reviewed the actual ten-file change after separating it from the branch rebase, then rechecked the full PR diff, relevant callers and history, project rules and domain terms, backward compatibility, route/tool architecture, and test changes. The prior two fixes remain valid and their threads remain resolved. I found no other issue.

This meets the late-round blocking bar: commit 6995b78 introduces it; an ordinary default config with a search key reaches it; and an operator who sees the green capability result but lacks the tool is given no accurate state or repair path, while the repository's invariant suite has no passing path until the contract is reconciled.

Verification: the unaffected launcher, routing, web-tool, runtime, and subagent regression set passed (292 passed). The capability suite failed (42 passed, 2 failed), both on the image_search registry/reporting mismatch. Ruff, diff whitespace, source-language, and large-file checks passed.

Comment thread raven/config/schema.py
"""The Serper key on the pre-vendor layout. Still honoured, read after
``tools.web.providers.serper.apiKey``; new writes go to the vendor slot."""
max_results: int = 5
images: bool = False

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: make the new image-search opt-in part of the capability contract.

With the new default images=false and a valid Serper key, the runtime does not register image_search, but raven.agent.tools.capabilities.is_configured and is_offered still return true because they only inspect the key. As a result, raven doctor prints a green check for a tool the agent does not have, and the extension setup surface suppresses its missing-capability guidance. The existing contract tests catch the same concrete divergence: tests/test_tool_capabilities.py now has 2 failures (42 pass). Please make the capability predicates/reporting account for this opt-in and preserve the registry invariant rather than weakening those tests.

This is introduced here, occurs in an ordinary keyed default config, and leaves the operator with neither a truthful diagnostic nor a repair path from that surface.

0xKT pushed a commit that referenced this pull request Sep 17, 2026
…ads (#463)

## Summary

`main` is red on three jobs (`python lint`, `unit 2/4`, `unit 3/4`), and
has been
since #451 merged. All of it is fallout from one arrival: `image_search`
came with a
second switch beside its credential.

`tools.web.search.images` decides whether the tool is registered at all
(`raven/agent/loop/wiring.py`), so a lane that never places a picture
keeps the tool
face it had. The capability table was not told. `is_configured` answers
on the vendor
key alone, so with a Serper key and the switch left off the table ticked
a capability
the agent does not hold, and the two tests that drive a real `AgentLoop`
caught exactly
the disagreement that module exists to prevent.

The switch belongs on `is_disabled`, not `is_configured`. That
function's own docstring
already names the reason: a switched-off tool usually has its credential
set, and
calling it unconfigured sends the deployer to set a key that is already
there.
`tools.disabledTools` and this flag are the same kind of answer, so
`is_offered` needs
no change and the doctor row reads "off" rather than "missing a key".

The test configs that mean "everything supplied" now turn the switch on,
the way
`raven/core/runtime.py` passes it from the config, and a new case pins
the combination
that was wrong: key present, pictures off, nothing offered, table saying
off.

Two more pieces of the same arrival:

- `ImageSearchTool` copied `WebSearchTool`'s key resolution without its
two attribute
annotations, so the untyped lambda `wiring.py` hands it left the
`api_key` property
returning `~AlwaysFalsy | str` against a declared `str`. The type
checker pinned in
  #416 reports it; the annotations are the fix.
- `WebSearchConfig` gained the `images` field, so the writer's validated
subtree now
carries it and two `test_config_update_tools` expectations that pin the
written
  section by value went stale.

Not changed here: `get_web_search` still reports `{provider, api_key,
max_results}` and
does not surface `images`, though `set_web_search` can write it. That is
a gap in the
read side of the config API, not a CI failure, and it wants its own
change.

## Type

- [x] Fix
- [ ] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

Run on Windows 11 in a clean worktree cut from `main` (`5a29ea22`):

- `uv run pytest tests/test_tool_capabilities.py
tests/test_config_update_tools.py -q`:
71 passed. All four tests failing on `main` now pass, plus the new case.
- `uv run --frozen --python 3.12 --all-extras ty check raven evolver
agents plugins-dist scripts`:
the `web.py:516` diagnostic is gone. The diagnostics that remain locally
are
unresolved imports for optional packages absent from this machine's
environment
(`boxlite`, `nio`); CI reported exactly one diagnostic before this
change, the one
  fixed here.
- `uv run --frozen --python 3.12 --extra dev ruff check raven tests`:
all checks passed.
- `ruff format --check` on the four changed files: already formatted.
- `uv run pytest tests/test_cli_doctor_commands.py
tests/test_rpc_console.py -q`, which
consume `is_disabled`: the failing-test set is byte-identical before and
after this
change (8 pre-existing Windows-only failures around file modes and an
absent browser
  package, none of them in the set CI reports).

## Risk

`is_disabled` gains a second reason to answer true, so a deployment with
a search key
and `tools.web.search.images` off now reads as "switched off" in `raven
doctor` and the
console capability report, where it previously read as offered. That is
the state the
loop was already in; only the description changes. Nothing about
registration, gating,
or the tool's runtime behaviour moves.

The other two changes are an annotation and two test expectations, with
no runtime
effect.

Rollback is reverting this commit; it touches nothing that other work
builds on.

## Related Issues

N/A

---------

Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
0xKT pushed a commit that referenced this pull request Sep 19, 2026
…ked again (#467)

## Summary

Two regressions in the hand-built deck lanes (medium and high on
Raven-Design), and the stream failure that made every run of them a coin
toss.

**The deck lane draws its own pages again.** PR #451 pointed the
hand-built lane at the engine's helper modules (`ppt_layout`,
`ppt_theme`, `ppt_shapes`), which were written for the max lane's script
backend. Their defaults became the deck: `heading()` put a white surface
band bled across every page, `card()` became the way anything was
placed, text frames were top-anchored with the lower third empty, and a
build script named three colours. Measured on the same request, the
accent colour appeared 8 times across 20 pages. The skill, route note
and assets reference go back to the pre-#451 text: the lane draws with
python-pptx and takes only the icon set, `add_formula` and `math_runs`
from the engine. On the reverted lane the accent appears 54 times, the
deck carries 12 real pictures and the layouts differ page to page.

**Colour is named beside the type sizes.** The skill listed sizes for
every role and said nothing about colour, so a lane with the right
palette still drew in one colour. One paragraph now says what colour is
for; the layouts note no longer reads as one accent per page, which a
lane took as a ceiling. Where `image_search` is not offered, the
fallback no longer tells the lane to give the pictures up: `web_search`
for the page, `web_fetch` it, and download where the fetch backend keeps
image links.

**A stall in a silent think is asked again.** Across two measured runs
the lane's turn failed 28 times on `Upstream idle timeout exceeded`,
every time in the one round that writes the whole build script into a
tool argument. `stream_llm_call` refuses to retry after output a watcher
has seen, and counted reasoning deltas and half-built tool-call
fragments as that output, though neither is ever shown as the reply. So
the configured retry ladder was never reached. `rendered()` now asks
what a watcher could have seen; every retry path resets the buffers
first (a slot left standing merged with the next attempt into a call the
model never made); a stall handed back as an error response carries the
usage it spent. The in-process builtin backend, whose comment claimed a
retry ladder and passed none, now receives the deployment's
`llmErrorRetryDelays` and `llmRetryAfterOutput` through the
SubagentManager. No packaged product takes that path; all five run as
ACP agents on the loop's own call site.

**Two bundled templates edited in place**, by the maintainer's word.
`beige_geometric_general_report` loses its page 20, a dark photograph
cut by a circle under pale rounded cards; 23 pages become 22 and the two
reference pages the lane borrows from it (17, 19) keep their numbers.
`black_circuit_tech_launch` loses the 27 percent circuit texture on its
master and the photograph on its Section Header layout, so its body and
section pages sit on the master's plain `#000000`; cover and closing are
unchanged. `templates.manifest.json` is repinned; `fetch_templates.py
--verify` holds all ten pins. The registry archive was not re-cut.

No configuration default changes. The only behaviour every lane sees: a
stream that fails before any reply text reached a watcher is retried on
the configured ladder instead of failing the turn.

## Type

- [x] Fix
- [ ] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

```
uv run --frozen pytest tests/test_agent_loop_llm_error_patience.py tests/test_agent_loop_stream.py \
  tests/test_subagent_manager.py tests/test_subagent_routing_backend.py -q
265 passed in 6.85s   (after rebase onto main)

uv run --frozen pytest tests/test_litellm_provider_stream.py tests/test_litellm_provider_stream_end.py \
  tests/test_provider_stream_fallback.py tests/test_providers_stream_idle_budget.py -q
102 passed

uv run --frozen ruff check raven/agent/loop/main.py raven/agent/subagent/backends/raven_loop.py \
  raven/agent/subagent/manager.py raven/providers/streaming.py
All checks passed!

grep -nP "[^\x00-\x7F]" agents/raven-design/route-raven-ppt.md   -> no matches (the note is pinned ASCII)
```

Both new tests were checked against the unfixed code:
`test_a_stall_during_a_silent_think_is_asked_again` fails with the old
`emitted` test, and
`test_what_the_failed_attempt_left_behind_is_dropped` fails with the
buffer resets removed.

The failure itself was reproduced outside the runtime: the same
37-message history, six pictures and tool table, sent through litellm
1.101.0 to the same vendor, succeeds on a short answer every time and
fails mid-stream one time in three when asked to write the full build
script; healthy long streams show a maximum inter-chunk gap of 1.9s over
234s, so the cut is a dropped connection, not a slow model.

Decks built on the changed lane, same request as the #451 acceptance run
(glm-5.3-flash via OpenRouter): medium 21 pages with 12 pictures and 7
text colours, 48 gate findings (4 `excessive_whitespace`, down from 18);
high 13 pages with 13 pictures and 9 text colours, 37 findings. The high
run's page count came from the host agent's forwarded brief, which
stated 10-14 pages the user never asked for; that is a separate issue.

Template edits: `tests/test_ppt_engine_templates.py
tests/test_ppt_engine_default_templates.py
tests/test_ppt_engine_template_bands.py
tests/test_ppt_engine_template_theme.py
tests/test_ppt_engine_measure_geometry.py` -> 152 passed, 1 skipped;
`make check-large-files` -> clean; both templates rendered and read page
by page.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [ ] User-facing docs or screenshots are updated when needed

## Risk

- [ ] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

User-visible: on every lane, a streamed call that fails during reasoning
or mid tool-call, with no reply text yet shown, waits out the configured
ladder (default 15/30/60s) and asks again instead of ending the turn. A
call that already streamed reply text behaves as before. Reasoning
tokens of the abandoned attempt are spent again and are now recorded in
usage. Rollback: revert the `providers` commit alone to restore the old
failure; the skill text and the builtin-backend plumbing are independent
of it.

## Related Issues

N/A
gloryfromca added a commit that referenced this pull request Sep 20, 2026
## Summary

`refactor/ui_web_architecture` last shared a commit with `main` at
`5a29ea22`
(#451). This merges `main` (`dbe259f6`) into it so the conflicts get
settled
once, now, instead of all at once under #475, and so CI runs on the
merged tree
rather than on a branch GitHub already reports as `CONFLICTING`.

Seven paths conflicted. How each was settled:

- `ui-web/src/features/settings/SettingsPage.tsx`, its test, and
`ui-web/src/live/120-settings.js` -- deleted on this branch, edited on
`main`
(modify/delete, so git leaves `main`'s copy in the tree). The rebuilt
settings
  surface replaces all three; the deletions stand.
- `i18n/messages.json` and `ui-tui/src/i18n/messages.generated.ts` --
`main`'s
  only change to either since the merge base is #506 dropping
`gui.set.chn.cron_to` and `cron_ph` (8 and 4 lines, no additions). This
branch
already drops the whole `gui.set.chn.*` group, so its side is the
superset and
  wins.
- `raven/skill_hub/hub.py` -- each side added one import from
  `skill_hub.audit`. `write_install_meta` is called at line 621,
`INSTALL_META` at line 699, and both exist in the merged `audit.py`, so
both
  are imported.
- `ui-web/build.py` -- `main` rewrote the `newline=""` comment, this
branch
  added `flush=True` to the print under it. Both kept.

What came in quietly and is worth knowing: #506's cron timezone fix
auto-merged
into `raven/rpc/methods/console.py` and brought its five cases in
`tests/test_rpc_settings.py` with it. The rebuilt settings pages have no
cron
section at all, so on this branch the backend can now write
`cron.defaultTimezone` correctly and nothing on the page does it.
Whether that
control gets ported, and in which PR, is a separate decision.

## Type

- [ ] Fix
- [ ] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [x] Other (catch-up merge)

## Verification

Run in a worktree on the merged commit:

- `uv run pytest tests/test_rpc_settings.py
tests/test_skill_hub_client.py tests/test_skill_hub_policy.py
tests/test_skill_hub_source.py tests/test_skill_hub_tools.py
tests/test_i18n_boundary.py -q` -- 174 passed.
- `npm test` in `ui-web` -- 181 files, 2513 tests, all passed.
- `node ui-tui/scripts/gen-i18n.mjs` -- regenerating the TUI catalogue
leaves
`messages.generated.ts` byte-identical to the resolved file, so the two
i18n
  artifacts agree.
- `make lint-python` -- 1986 files already formatted, clean.
- `make lint-imports` -- 10 contracts kept, 0 broken.
- `make build-ui` -- page built, `boot-snapshot: OK` on both snapshots.
- `scripts/check_source_language.py` and `scripts/check_large_files.py`
over
  `bc74bf73..HEAD` -- both exit 0.

The full suite was left to CI.

`commit messages` is red, and will be red on any PR that merges `main`
into a
branch. Commitlint runs over `<base>..HEAD`, which here includes every
commit
`main` gained since the merge base. One of them, `b01534d0` (#471,
merged
09-18), carries a `Co-authored-by` trailer with a non-ASCII name, which
`scripts/check_commit_messages.py` rejects. That is history on `main`;
this
branch neither introduced it nor can rewrite it. Nothing in the ruleset
makes
that check blocking.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [ ] User-facing docs or screenshots are updated when needed

## Risk

This branch is squash-only, so merging this PR flattens the merge commit
and
the merge base stays at `5a29ea22`. The contents line up either way, but
git
will not know `main` was taken in, and the next catch-up will re-raise
the
content conflicts in the two large i18n files. Landing it as a real
merge
commit instead would move the merge base and avoid that; that needs
someone who
can bypass the ruleset.

Rollback is resetting the branch to `bc74bf73`; nothing here is a source
change
of its own beyond the four resolutions above.

- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

## Related Issues

N/A. Unblocks #475.

---------

Co-authored-by: xfng-sd <xufang@shanda.com>
Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
Co-authored-by: KT <74288668+0xKT@users.noreply.github.com>
Co-authored-by: Handsome-wzw <68996445+Handsome-wzw@users.noreply.github.com>
Co-authored-by: Dizhan Xue <dizhan.xue@evermind.ai>
Co-authored-by: Kevin Hu <kevinhu.sh@gmail.com>
Co-authored-by: admin <admin@SH-HuKai.local>
Co-authored-by: 江国庆/Forrest <mr.jianggq@163.com>
Co-authored-by: 江国庆 <guoqingjiang@deepglint.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: ypflll <ypflll@163.com>
Co-authored-by: yao pengfei <yaopengfei@shanda.com>
Co-authored-by: Zuyi Zhou <144661423+ZuyiZhou@users.noreply.github.com>
Co-authored-by: Tong Li <litong02@shanda.com>
Co-authored-by: litong <238663200+TongLi31@users.noreply.github.com>
Co-authored-by: Tchen-data <176354753+Tchen-data@users.noreply.github.com>
Co-authored-by: gloryfromca <23442919+gloryfromca@users.noreply.github.com>
Co-authored-by: Dizhan Xue <xuedizhan17@mails.ucas.ac.cn>
LivXue pushed a commit that referenced this pull request Oct 12, 2026
… one .pptx delivered (#451)

## Summary

The hand-built deck lane (Raven-Design at medium and high) and the deck
engine lane (Raven-PPT at max) as one product, measured on live runs.

- image_search for the deck lanes sits behind tools.web.search.images,
off by default and switched on by the design launcher; the readiness
probe for the deck route reads the target lane's own picture vendor; the
build-interpreter shim has a .cmd twin for native Windows.
- Deck route on Raven-Design: page furniture comes from the engine's
helper modules (ppt_layout, ppt_theme, ppt_icons, ppt_shapes,
ppt_charts) written beside the build script; diagrams, picture treatment
and cover/section compositions are drawn by hand. Five recorded runs had
each rewritten the same 48 helpers.
- Deck route keeps no anti-pattern ledger, contract or task state: nine
audited runs wrote the ledger 18 times and read it back never, and every
real fix came from reading a render. The editorial skill is read
alongside the deck skill; every page is read back from a render at 90
dpi or more before delivery.
- Defaults before questions: language of the request, general audience,
about 20 pages, light ground, memory first; ask at most once. Two of
three ask_user calls in the audit timed out unanswered.
- Formulas as pictures (matplotlib mathtext, dfrac promotion, serif
scale) and inline sub/superscript runs, shared by both lanes;
image_search for both lanes keyed by the selected search vendor; the
raven-python interpreter shim (sh wrapper, not a symlink); all three
tiers at 400 iterations.
- layouts.md gains nine compositions measured off the packaged
templates' reference pages; three rules ask for full components, a
hierarchy among them, and the whole page used.
- The max lane delivers the .pptx alone (D64); the PDF render stays the
engine's own preview.
- ppt-engine: template gates silenced on template pages, borrowed
template files kept apart from borrowed page numbers, the icon pen
follows the icon's side.

## Type

- [x] Fix
- [ ] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

```
uv run --frozen pytest -q -x tests/test_agents_design_launcher.py tests/test_subagent_routing_backend.py tests/test_ppt_engine_build_tool.py tests/test_ppt_engine_build_stage.py tests/test_ppt_engine_assets_formulas.py tests/test_ppt_engine_gates_registry.py
# 299 passed
```

Live runs on a test stack (medium tier, glm-5.3-flash, the
eight-character request "make a deck introducing computer vision"): with
the editorial skill read, 20 pages, 18 of them carrying a searched
picture or a paper figure, delivered in 26 minutes with no question
asked, no task-state call, no ledger, no hand-written helper, renders at
90 dpi.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [ ] User-facing docs or screenshots are updated when needed

## Risk

User-visible: the max lane no longer delivers a PDF beside the deck; the
design lane asks fewer questions; the host loop offers image_search only
where tools.web.search.images is on. Rollback: revert the commits
touching agents/raven-design, raven/agent and
plugins-dist/ppt-engine/raven_ppt/stages/build.py; no data migration is
involved.

- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

## Related Issues

N/A
LivXue pushed a commit that referenced this pull request Oct 12, 2026
…ads (#463)

## Summary

`main` is red on three jobs (`python lint`, `unit 2/4`, `unit 3/4`), and
has been
since #451 merged. All of it is fallout from one arrival: `image_search`
came with a
second switch beside its credential.

`tools.web.search.images` decides whether the tool is registered at all
(`raven/agent/loop/wiring.py`), so a lane that never places a picture
keeps the tool
face it had. The capability table was not told. `is_configured` answers
on the vendor
key alone, so with a Serper key and the switch left off the table ticked
a capability
the agent does not hold, and the two tests that drive a real `AgentLoop`
caught exactly
the disagreement that module exists to prevent.

The switch belongs on `is_disabled`, not `is_configured`. That
function's own docstring
already names the reason: a switched-off tool usually has its credential
set, and
calling it unconfigured sends the deployer to set a key that is already
there.
`tools.disabledTools` and this flag are the same kind of answer, so
`is_offered` needs
no change and the doctor row reads "off" rather than "missing a key".

The test configs that mean "everything supplied" now turn the switch on,
the way
`raven/core/runtime.py` passes it from the config, and a new case pins
the combination
that was wrong: key present, pictures off, nothing offered, table saying
off.

Two more pieces of the same arrival:

- `ImageSearchTool` copied `WebSearchTool`'s key resolution without its
two attribute
annotations, so the untyped lambda `wiring.py` hands it left the
`api_key` property
returning `~AlwaysFalsy | str` against a declared `str`. The type
checker pinned in
  #416 reports it; the annotations are the fix.
- `WebSearchConfig` gained the `images` field, so the writer's validated
subtree now
carries it and two `test_config_update_tools` expectations that pin the
written
  section by value went stale.

Not changed here: `get_web_search` still reports `{provider, api_key,
max_results}` and
does not surface `images`, though `set_web_search` can write it. That is
a gap in the
read side of the config API, not a CI failure, and it wants its own
change.

## Type

- [x] Fix
- [ ] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

Run on Windows 11 in a clean worktree cut from `main` (`5a29ea22`):

- `uv run pytest tests/test_tool_capabilities.py
tests/test_config_update_tools.py -q`:
71 passed. All four tests failing on `main` now pass, plus the new case.
- `uv run --frozen --python 3.12 --all-extras ty check raven evolver
agents plugins-dist scripts`:
the `web.py:516` diagnostic is gone. The diagnostics that remain locally
are
unresolved imports for optional packages absent from this machine's
environment
(`boxlite`, `nio`); CI reported exactly one diagnostic before this
change, the one
  fixed here.
- `uv run --frozen --python 3.12 --extra dev ruff check raven tests`:
all checks passed.
- `ruff format --check` on the four changed files: already formatted.
- `uv run pytest tests/test_cli_doctor_commands.py
tests/test_rpc_console.py -q`, which
consume `is_disabled`: the failing-test set is byte-identical before and
after this
change (8 pre-existing Windows-only failures around file modes and an
absent browser
  package, none of them in the set CI reports).

## Risk

`is_disabled` gains a second reason to answer true, so a deployment with
a search key
and `tools.web.search.images` off now reads as "switched off" in `raven
doctor` and the
console capability report, where it previously read as offered. That is
the state the
loop was already in; only the description changes. Nothing about
registration, gating,
or the tool's runtime behaviour moves.

The other two changes are an annotation and two test expectations, with
no runtime
effect.

Rollback is reverting this commit; it touches nothing that other work
builds on.

## Related Issues

N/A

---------

Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
LivXue pushed a commit that referenced this pull request Oct 12, 2026
…ked again (#467)

## Summary

Two regressions in the hand-built deck lanes (medium and high on
Raven-Design), and the stream failure that made every run of them a coin
toss.

**The deck lane draws its own pages again.** PR #451 pointed the
hand-built lane at the engine's helper modules (`ppt_layout`,
`ppt_theme`, `ppt_shapes`), which were written for the max lane's script
backend. Their defaults became the deck: `heading()` put a white surface
band bled across every page, `card()` became the way anything was
placed, text frames were top-anchored with the lower third empty, and a
build script named three colours. Measured on the same request, the
accent colour appeared 8 times across 20 pages. The skill, route note
and assets reference go back to the pre-#451 text: the lane draws with
python-pptx and takes only the icon set, `add_formula` and `math_runs`
from the engine. On the reverted lane the accent appears 54 times, the
deck carries 12 real pictures and the layouts differ page to page.

**Colour is named beside the type sizes.** The skill listed sizes for
every role and said nothing about colour, so a lane with the right
palette still drew in one colour. One paragraph now says what colour is
for; the layouts note no longer reads as one accent per page, which a
lane took as a ceiling. Where `image_search` is not offered, the
fallback no longer tells the lane to give the pictures up: `web_search`
for the page, `web_fetch` it, and download where the fetch backend keeps
image links.

**A stall in a silent think is asked again.** Across two measured runs
the lane's turn failed 28 times on `Upstream idle timeout exceeded`,
every time in the one round that writes the whole build script into a
tool argument. `stream_llm_call` refuses to retry after output a watcher
has seen, and counted reasoning deltas and half-built tool-call
fragments as that output, though neither is ever shown as the reply. So
the configured retry ladder was never reached. `rendered()` now asks
what a watcher could have seen; every retry path resets the buffers
first (a slot left standing merged with the next attempt into a call the
model never made); a stall handed back as an error response carries the
usage it spent. The in-process builtin backend, whose comment claimed a
retry ladder and passed none, now receives the deployment's
`llmErrorRetryDelays` and `llmRetryAfterOutput` through the
SubagentManager. No packaged product takes that path; all five run as
ACP agents on the loop's own call site.

**Two bundled templates edited in place**, by the maintainer's word.
`beige_geometric_general_report` loses its page 20, a dark photograph
cut by a circle under pale rounded cards; 23 pages become 22 and the two
reference pages the lane borrows from it (17, 19) keep their numbers.
`black_circuit_tech_launch` loses the 27 percent circuit texture on its
master and the photograph on its Section Header layout, so its body and
section pages sit on the master's plain `#000000`; cover and closing are
unchanged. `templates.manifest.json` is repinned; `fetch_templates.py
--verify` holds all ten pins. The registry archive was not re-cut.

No configuration default changes. The only behaviour every lane sees: a
stream that fails before any reply text reached a watcher is retried on
the configured ladder instead of failing the turn.

## Type

- [x] Fix
- [ ] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

```
uv run --frozen pytest tests/test_agent_loop_llm_error_patience.py tests/test_agent_loop_stream.py \
  tests/test_subagent_manager.py tests/test_subagent_routing_backend.py -q
265 passed in 6.85s   (after rebase onto main)

uv run --frozen pytest tests/test_litellm_provider_stream.py tests/test_litellm_provider_stream_end.py \
  tests/test_provider_stream_fallback.py tests/test_providers_stream_idle_budget.py -q
102 passed

uv run --frozen ruff check raven/agent/loop/main.py raven/agent/subagent/backends/raven_loop.py \
  raven/agent/subagent/manager.py raven/providers/streaming.py
All checks passed!

grep -nP "[^\x00-\x7F]" agents/raven-design/route-raven-ppt.md   -> no matches (the note is pinned ASCII)
```

Both new tests were checked against the unfixed code:
`test_a_stall_during_a_silent_think_is_asked_again` fails with the old
`emitted` test, and
`test_what_the_failed_attempt_left_behind_is_dropped` fails with the
buffer resets removed.

The failure itself was reproduced outside the runtime: the same
37-message history, six pictures and tool table, sent through litellm
1.101.0 to the same vendor, succeeds on a short answer every time and
fails mid-stream one time in three when asked to write the full build
script; healthy long streams show a maximum inter-chunk gap of 1.9s over
234s, so the cut is a dropped connection, not a slow model.

Decks built on the changed lane, same request as the #451 acceptance run
(glm-5.3-flash via OpenRouter): medium 21 pages with 12 pictures and 7
text colours, 48 gate findings (4 `excessive_whitespace`, down from 18);
high 13 pages with 13 pictures and 9 text colours, 37 findings. The high
run's page count came from the host agent's forwarded brief, which
stated 10-14 pages the user never asked for; that is a separate issue.

Template edits: `tests/test_ppt_engine_templates.py
tests/test_ppt_engine_default_templates.py
tests/test_ppt_engine_template_bands.py
tests/test_ppt_engine_template_theme.py
tests/test_ppt_engine_measure_geometry.py` -> 152 passed, 1 skipped;
`make check-large-files` -> clean; both templates rendered and read page
by page.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [ ] User-facing docs or screenshots are updated when needed

## Risk

- [ ] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

User-visible: on every lane, a streamed call that fails during reasoning
or mid tool-call, with no reply text yet shown, waits out the configured
ladder (default 15/30/60s) and asks again instead of ending the turn. A
call that already streamed reply text behaves as before. Reasoning
tokens of the abandoned attempt are spent again and are now recorded in
usage. Rollback: revert the `providers` commit alone to restore the old
failure; the skill text and the builtin-backend plumbing are independent
of it.

## Related Issues

N/A
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants